package com.wh1200.mybatis.xmlless.handler

import com.wh1200.jackson.createObjectMapper
import com.wh1200.mybatis.xmlless.enums.XmllessDbType
import com.wh1200.mybatis.xmlless.util.DbTypeHolder
import com.fasterxml.jackson.databind.JavaType
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer
import org.apache.ibatis.type.BaseTypeHandler
import org.apache.ibatis.type.JdbcType
import org.apache.ibatis.type.MappedTypes
import org.postgresql.util.PGobject
import java.sql.CallableStatement
import java.sql.PreparedStatement
import java.sql.ResultSet
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.format.DateTimeFormatter


/**
 * json字段处理器
 *
 * @author 吴昊
 * @date 2020/06/18
 * @version 1.0
 * @since v1.0.0
 */
@MappedTypes(String::class)
open class JsonTypeHandler() : BaseTypeHandler<Any>() {

  private val mapper = createMapper()
  private var type: JavaType? = null

  companion object {
    const val JSON_TYPE: String = "json"
  }

  constructor(type: JavaType?) : this() {
    this.type = type
  }

  override fun getNullableResult(rs: ResultSet, column: String): Any? {
    val columnValue = rs.getString(column)
    return if (columnValue == null) {
      null
    } else {
      toTypeValue(columnValue)
    }
  }

  override fun getNullableResult(rs: ResultSet, columnIndex: Int): Any? {
    val str = rs.getString(columnIndex)
    return toTypeValue(str)
  }

  override fun getNullableResult(cs: CallableStatement, columnIndex: Int): Any? {
    val str = cs.getString(columnIndex)
    return toTypeValue(str)
  }

  override fun setNonNullParameter(ps: PreparedStatement, i: Int, parameter: Any?, jdbcType: JdbcType?) {
    val jsonStr = mapper.writeValueAsString(parameter)
    if (DbTypeHolder.getDBType() == XmllessDbType.POSTGRE_SQL) {
      val pGobject = PGobject()
      pGobject.type = JSON_TYPE
      pGobject.value = jsonStr
      ps.setObject(i, pGobject)
    } else {
      ps.setString(i, jsonStr)
    }
  }

  private fun createMapper(): ObjectMapper {
    val mapper = createObjectMapper()
    val javaTimeModule = JavaTimeModule()
    javaTimeModule.addSerializer(
        LocalDateTime::class.java,
        LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
    )
    javaTimeModule.addSerializer(LocalDate::class.java, LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")))
    javaTimeModule.addSerializer(LocalTime::class.java, LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")))
    mapper.registerModule(javaTimeModule)
    return mapper
  }

  private fun toTypeValue(str: String): Any? {
    if (type == null) {
      return mapper.readValue(str, Any::class.java)
    }
    return mapper.readValue(str, type)
  }

}
